#pragma once

#include <mcp/config.h>

#include <memory>

namespace mcp {

/// @brief C++11 compatible optional type
template<typename T>
class Optional {
public:
    Optional() : has_value_(false) {}

    Optional(const T& value) : has_value_(true), value_(new T(value)) {}

    Optional(T&& value) : has_value_(true), value_(new T(std::move(value))) {}

    Optional(const Optional& other) : has_value_(other.has_value_) {
        if (has_value_) {
            value_ = std::unique_ptr<T>(new T(*other.value_));
        }
    }

    Optional(Optional&& other) noexcept : has_value_(other.has_value_), value_(std::move(other.value_)) {
        other.has_value_ = false;
    }

    Optional& operator=(const Optional& other) {
        if (this != &other) {
            has_value_ = other.has_value_;
            if (has_value_) {
                value_ = std::unique_ptr<T>(new T(*other.value_));
            } else {
                value_.reset();
            }
        }
        return *this;
    }

    Optional& operator=(Optional&& other) noexcept {
        if (this != &other) {
            has_value_ = other.has_value_;
            value_ = std::move(other.value_);
            other.has_value_ = false;
        }
        return *this;
    }

    explicit operator bool() const noexcept { return has_value_; }

    bool has_value() const noexcept { return has_value_; }

    
    T& value() & {
        if (!has_value_) throw std::runtime_error("Optional has no value");
        return *value_;
    }

    const T& value() const & {
        if (!has_value_) throw std::runtime_error("Optional has no value");
        return *value_;
    }

    T&& value() && {
        if (!has_value_) throw std::runtime_error("Optional has no value");
        return std::move(*value_);
    }

    T* operator->() { return &value(); }

    const T* operator->() const { return &value(); }

    T& operator*() { return value(); }

    const T& operator*() const { return value(); }

private:
    bool has_value_;
    std::unique_ptr<T> value_;
};

/// @brief C++11 compatible nullopt type
struct NoneType {};

constexpr NoneType None = {};

} // namespace mcp